﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;

namespace Scheduling.Models
{
    public class IoC
    {
        #region Methods
        public int Version { get { return 3; } }
        public DateTime VersionDate { get { return new DateTime(2011, 10, 3); } }
        public static void CreateEmptyContainer()
        {
            Container = new UnityishContainer();
        }

        /// <summary>
        /// Pre: Container created
        /// </summary>
        [DebuggerStepThrough]
        public static void RegisterType<TFrom, TTo>(params InjectionMemberish[] injectionMembers)
            where TTo : TFrom
        {
            Assert(Container != null, "IoC: Call CreateEmptyContainer first to initialize container.");
            Container.RegisterType<TFrom, TTo>(injectionMembers);
        }

        /// <summary>
        /// Pre: Container created
        /// </summary>
        /*
        [DebuggerStepThrough]
        public static void RegisterInstance<TInterface>(TInterface instance)
        {
            Assert(Container != null, "IoC: Call CreateEmptyContainer first to initialize container.");
            Container.RegisterInstance<TInterface>(instance);
        }
        */

        /// <summary>
        /// Pre: Container created, T registered
        /// </summary>
        [DebuggerStepThrough]
        public static T Resolve<T>()
        {
            Assert(Container != null, "IoC: Call CreateEmptyContainer first to initialize container.");
            return Container.Resolve<T>();
        }
        #endregion Methods

        #region TDD support
        /*
        [DebuggerStepThrough]
        public static void ResetForTDD()
        {
            Container = null;
        }
        */
        #endregion TDD support

        #region Private support
        #region Properties
        private static IUnityishContainer Container { get; set; }
        #endregion Properties

        #region Methods
        private static void Assert(bool condition, string message)
        {
            if (!condition)
            {
                throw new InvalidOperationException(message);
            }
        }
        #endregion Methods
        #endregion Private support
    }

    interface IUnityishContainer
    {
        void RegisterInstance<TInterface>(object instance);
        void RegisterType<TFrom, TTo>(InjectionMemberish[] injectionMembers);
        T Resolve<T>();
    }

    class UnityishContainer : IUnityishContainer
    {
        Dictionary<Type, IUnityishMapping> _mappings = new Dictionary<Type, IUnityishMapping>();

        public void RegisterInstance<TInterface>(object instance)
        {
            _mappings[typeof(TInterface)] = new InstanceMapping(instance);
        }

        public void RegisterType<TFrom, TTo>(InjectionMemberish[] injectionMembers)
        {
            _mappings[typeof(TFrom)] = new TypeMapping(typeof(TTo));
        }

        public T Resolve<T>()
        {
            return Resolve<T>("(none)");
        }

        private T Resolve<T>(string name)
        {
            if (!_mappings.Keys.Contains(typeof(T)))
            {
                throw new ResolutionFailedException(
                    string.Format("Resolution of the dependency failed. Resolving {0},{1}{2}", typeof(T), name, NewLine));
            }
            return (T)_mappings[typeof(T)].Resolve();
        }

        private string NewLine { get { return "\r\n"; } }
    }

    public class InjectionMemberish { }
    class ResolutionFailedException : Exception
    {
        public ResolutionFailedException(string message)
            : base(message)
        { }

        public ResolutionFailedException(string message, Exception exception)
            : base(message, exception)
        { }

        public override string Message
        {
            get
            {
                return base.Message;
            }
        }
    }

    interface IUnityishMapping
    {
        object Resolve();
    }

    class InstanceMapping : IUnityishMapping
    {
        private object _instance;

        public InstanceMapping(object instance)
        {
            _instance = instance;
        }

        public object Resolve()
        {
            return _instance;
        }
    }

    class TypeMapping : IUnityishMapping
    {
        private Type _type;

        public TypeMapping(Type type)
        {
            _type = type;
        }
        public object Resolve()
        {
            return Activator.CreateInstance(_type);
        }
    }
}
